﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using Modbus.Device;

namespace WindowsFormsApp1
{
    class ModbusRTU : IDriver 
    {
        private SerialPort _serialPort;
        private int _id;
        private string _name;
        private IModbusMaster _modbusMaster;

        /// <summary>
        /// 当前客户端的数据变换机制，当你需要从字节数据转换类型数据的时候需要。
        /// </summary>
        public ReverseWordTransform ByteTransform { get; set; }

        public ModbusRTU(int id, string name, SerialPort serialPort)
        {
            _id = id;
            _name = name;
            _serialPort = serialPort;
        }

        public int ID { get { return _id; } }

        public string Name { get { return _name; } }

        public bool IsConnected =>true;

        public  bool Connect()
        {
            if (_serialPort.IsOpen)
            {
                
                return true;
            }
                
            else
            {
                try
                {

                    _serialPort.Open();
                    _modbusMaster = ModbusSerialMaster.CreateRtu(_serialPort);
                    return true;
                }
                catch (Exception ex)
                {
                    return false;
                    throw new Exception(ex.ToString());
                }
            }
        }

        #region Read

        public OperateResult<byte[]> Read(string address, ushort length)
        {
            OperateResult<ModbusAddress> analysis = AnalysisReadAddress(address);
            if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(analysis);
            byte station = (byte)analysis.Content.Station;
            int function = analysis.Content.Function;
            ushort startAddress = analysis.Content.Address;
            try
            {
                byte[] result = null;
                if (function == 0x03)
                {
                    result = ByteTransform.TransByte(_modbusMaster.ReadHoldingRegisters(station, startAddress, length));
                }
                else if(function ==0x04)
                {
                    result = ByteTransform.TransByte(_modbusMaster.ReadInputRegisters(station, startAddress, length));
                }
                return OperateResult.CreateSuccessResult(result);
            }
            catch (Exception ex)
            {
                return OperateResult.CreateFailedResult<byte[]>(new OperateResult(ex.ToString()));
            }
        }

        public OperateResult<bool> ReadBool(string address)
        {
            OperateResult<ModbusAddress> analysis = AnalysisReadAddress(address);
            if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<bool>(analysis);
            byte station = (byte)analysis.Content.Station;
            int function = analysis.Content.Function;
            ushort startAddress = analysis.Content.Address;
            try
            {
                bool[] result;
                if (function==0x01)
                {
                    result = _modbusMaster.ReadCoils(station, startAddress, 1);
                }
                else
                {
                    result = _modbusMaster.ReadInputs(station, startAddress, 1);
                }                                      
                return OperateResult.CreateSuccessResult(result[0]);
            }
            catch (Exception ex)
            {
                return OperateResult.CreateFailedResult<bool>(new OperateResult(ex.ToString()));
            }                    
        }

        public OperateResult<bool[]> ReadBool(string address, ushort length)
        {
            OperateResult<ModbusAddress> analysis = AnalysisReadAddress(address);
            if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<bool[]>(analysis);
            byte station = (byte)analysis.Content.Station;
            int function = analysis.Content.Function;
            ushort startAddress = analysis.Content.Address;
            try
            {
                bool[] result=null;
                if (function == 0x01)
                {
                    result = _modbusMaster.ReadCoils(station, startAddress, length);
                }
                else if(function==0x02)
                {
                    result = _modbusMaster.ReadInputs(station, startAddress, length);
                }
                return OperateResult.CreateSuccessResult(result);
            }
            catch (Exception ex)
            {
                return OperateResult.CreateFailedResult<bool[]>(new OperateResult(ex.ToString()));
            }
        }

        public OperateResult<T> ReadCustomer<T>(string address) where T : IDataTransfer, new()
        {
            throw new NotImplementedException();
        }

        public OperateResult<double> ReadDouble(string address)
        {
            OperateResult<double[]> temp = ReadDouble(address, 1);
            return OperateResult.CreateSuccessResult(temp.Content.First());
        }

        public OperateResult<double[]> ReadDouble(string address, ushort length)
        {
            OperateResult<byte[]> buffer = Read(address, length);
            if (!buffer.IsSuccess) return OperateResult.CreateFailedResult<double[]>(buffer);
            double[] result = ByteTransform.TransDouble(buffer.Content, 0, length);
            return OperateResult.CreateSuccessResult(result);
        }

        public OperateResult<float> ReadFloat(string address)
        {
            OperateResult<float[]> temp = ReadFloat(address, 1);
            return OperateResult.CreateSuccessResult(temp.Content.First());
        }

        public OperateResult<float[]> ReadFloat(string address, ushort length)
        {
            OperateResult<byte[]> buffer = Read(address, length);
            if (!buffer.IsSuccess) return OperateResult.CreateFailedResult<float[]>(buffer);
            float[] result = ByteTransform.TransSingle(buffer.Content, 0, length);
            return OperateResult.CreateSuccessResult(result);
        }

        public OperateResult<short> ReadInt16(string address)
        {
            OperateResult<short[]> temp = ReadInt16(address, 1);
            return OperateResult.CreateSuccessResult(temp.Content.First());
        }

        public OperateResult<short[]> ReadInt16(string address, ushort length)
        {
           OperateResult<byte[]>buffer= Read(address, length);
            if (!buffer.IsSuccess) return OperateResult.CreateFailedResult<short[]>(buffer);
            short[] result = ByteTransform.TransInt16(buffer.Content, 0, length);
            return OperateResult.CreateSuccessResult(result);
        }

        public OperateResult<int> ReadInt32(string address)
        {
            OperateResult<int[]> temp = ReadInt32(address, 1);
            return OperateResult.CreateSuccessResult(temp.Content.First());
        }

        public OperateResult<int[]> ReadInt32(string address, ushort length)
        {
            OperateResult<byte[]> buffer = Read(address, length);
            if (!buffer.IsSuccess) return OperateResult.CreateFailedResult<int[]>(buffer);
            int[] result = ByteTransform.TransInt32(buffer.Content, 0, length);
            return OperateResult.CreateSuccessResult(result);
        }

        public OperateResult<long> ReadInt64(string address)
        {
            OperateResult<long[]> temp = ReadInt64(address, 1);
            return OperateResult.CreateSuccessResult(temp.Content.First());
        }

        public OperateResult<long[]> ReadInt64(string address, ushort length)
        {
            OperateResult<byte[]> buffer = Read(address, length);
            if (!buffer.IsSuccess) return OperateResult.CreateFailedResult<long[]>(buffer);
            long[] result = ByteTransform.TransInt64(buffer.Content, 0, length);
            return OperateResult.CreateSuccessResult(result);
        }

        public OperateResult<string> ReadString(string address, ushort length)
        {
            OperateResult<byte[]> buffer = Read(address, length);
            if (!buffer.IsSuccess) return OperateResult.CreateFailedResult<string>(buffer);
            string result = ByteTransform.TransString(buffer.Content, 0, length,Encoding.ASCII);
            return OperateResult.CreateSuccessResult(result);
        }

        public OperateResult<ushort> ReadUInt16(string address)
        {
            OperateResult<ushort[]> temp = ReadUInt16(address, 1);
            return OperateResult.CreateSuccessResult(temp.Content.First());
        }

        public OperateResult<ushort[]> ReadUInt16(string address, ushort length)
        {
            OperateResult<byte[]> buffer = Read(address, length);
            if (!buffer.IsSuccess) return OperateResult.CreateFailedResult<ushort[]>(buffer);
            ushort[] result = ByteTransform.TransUInt16(buffer.Content, 0, length);
            return OperateResult.CreateSuccessResult(result);
        }

        public OperateResult<uint> ReadUInt32(string address)
        {
            OperateResult<uint[]> temp = ReadUInt32(address, 1);
            return OperateResult.CreateSuccessResult(temp.Content.First());
        }

        public OperateResult<uint[]> ReadUInt32(string address, ushort length)
        {
            OperateResult<byte[]> buffer = Read(address, length);
            if (!buffer.IsSuccess) return OperateResult.CreateFailedResult<uint[]>(buffer);
            uint[] result = ByteTransform.TransUInt32(buffer.Content, 0, length);
            return OperateResult.CreateSuccessResult(result);
        }

        public OperateResult<ulong> ReadUInt64(string address)
        {
            OperateResult<ulong[]> temp = ReadUInt64(address, 1);
            return OperateResult.CreateSuccessResult(temp.Content.First());
        }

        public OperateResult<ulong[]> ReadUInt64(string address, ushort length)
        {
            OperateResult<byte[]> buffer = Read(address, length);
            if (!buffer.IsSuccess) return OperateResult.CreateFailedResult<ulong[]>(buffer);
            ulong[] result = ByteTransform.TransUInt64(buffer.Content, 0, length);
            return OperateResult.CreateSuccessResult(result);
        }
        #endregion

        #region Write
        public OperateResult Write(string address, byte[] value)
        {
            OperateResult<ModbusAddress> analysis = AnalysisReadAddress(address);
            if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(analysis);
            byte station = (byte)analysis.Content.Station;
            int function = analysis.Content.Function;
            ushort startAddress = analysis.Content.Address;
            int length = value.Length >> 1;
            if (value.Length % 2 != 0) length += 1;
            try
            {
                ushort[] data = ByteTransform.TransUInt16(value,0,length);
                if (function == 0x10)
                {
                     _modbusMaster.WriteMultipleRegisters(station, startAddress, data);
                    
                }
                else if (function==0x06)
                {
                    _modbusMaster.WriteSingleRegister(station, startAddress, data[0]);
                }
                return OperateResult.CreateSuccessResult();
            }
            catch (Exception ex)
            {
                return OperateResult.CreateFailedResult<byte[]>(new OperateResult(ex.ToString()));
            }
        }

        public OperateResult Write(string address, short value)
        {
            byte[] data = ByteTransform.TransByte(value);
           return Write(address, data);
        }

        public OperateResult Write(string address, short[] values)
        {
            byte[] data = ByteTransform.TransByte(values);
            return Write(address, data);
        }

        public OperateResult Write(string address, int value)
        {
            byte[] data = ByteTransform.TransByte(value);
            return Write(address, data);
        }

        public OperateResult Write(string address, int[] values)
        {
            byte[] data = ByteTransform.TransByte(values);
            return Write(address, data);
        }

        public OperateResult Write(string address, long value)
        {
            byte[] data = ByteTransform.TransByte(value);
            return Write(address, data);
        }

        public OperateResult Write(string address, long[] values)
        {
            byte[] data = ByteTransform.TransByte(values);
            return Write(address, data);
        }

        public OperateResult Write(string address, float value)
        {
            byte[] data = ByteTransform.TransByte(value);
            return Write(address, data);
        }

        public OperateResult Write(string address, float[] values)
        {
            byte[] data = ByteTransform.TransByte(values);
            return Write(address, data);
        }

        public OperateResult Write(string address, double value)
        {
            byte[] data = ByteTransform.TransByte(value);
            return Write(address, data);
        }

        public OperateResult Write(string address, double[] values)
        {
            byte[] data = ByteTransform.TransByte(values);
            return Write(address, data);
        }

        public OperateResult Write(string address, string value)
        {
            byte[] data = ByteTransform.TransByte(value,Encoding.ASCII);
            return Write(address, data);
        }

        public OperateResult Write(string address, string value, int length)
        {          
            return Write(address, value);
        }

        public OperateResult Write(string address,bool value)
        {
            OperateResult<ModbusAddress> analysis = AnalysisReadAddress(address);
            if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<bool[]>(analysis);
            byte station = (byte)analysis.Content.Station;
            int function = analysis.Content.Function;
            ushort startAddress = analysis.Content.Address;
            try
            {
                if (function == 0x05)
                {
                    _modbusMaster.WriteSingleCoil(station, startAddress, value);
                }
                return OperateResult.CreateSuccessResult();
            }
            catch (Exception ex)
            {
                return OperateResult.CreateFailedResult<bool[]>(new OperateResult(ex.ToString()));
            }
        }

        public OperateResult Write(string address, bool[] values)
        {
            OperateResult<ModbusAddress> analysis = AnalysisReadAddress(address);
            if (!analysis.IsSuccess) return OperateResult.CreateFailedResult<bool[]>(analysis);
            byte station = (byte)analysis.Content.Station;
            int function = analysis.Content.Function;
            ushort startAddress = analysis.Content.Address;
            try
            {            
                if (function == 0x10)
                {
                    _modbusMaster.WriteMultipleCoils(station, startAddress, values);
                }
                return OperateResult.CreateSuccessResult();
            }
            catch (Exception ex)
            {
                return OperateResult.CreateFailedResult<bool[]>(new OperateResult(ex.ToString()));
            }
        }

        public OperateResult WriteCustomer<T>(string address, T value) where T : IDataTransfer, new()
        {
            throw new NotImplementedException();
        }
        #endregion

        private OperateResult<ModbusAddress> AnalysisReadAddress(string address, bool isStartWithZero=true)
        {
            try
            {
                ModbusAddress mAddress = new ModbusAddress(address);
                if (!isStartWithZero)
                {
                    if (mAddress.Address < 1) throw new Exception(StringResources.Language.ModbusAddressMustMoreThanOne);
                    mAddress.Address = (ushort)(mAddress.Address - 1);
                }
                return OperateResult.CreateSuccessResult(mAddress);
            }
            catch (Exception ex)
            {
                return new OperateResult<ModbusAddress>() { Message = ex.Message };
            }
        }
    }
}
